package com.yjt.common.log;


import com.alibaba.fastjson.JSON;
import com.yjt.common.entity.oauth.CommonUser;
import com.yjt.common.log.annotation.OperLog;
import com.yjt.common.log.dao.NorLogDao;
import com.yjt.common.log.entity.NormalLog;
import com.yjt.common.utils.SecurityUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

@Aspect
@Component
public class OperLogAspect {
    /**
     * 操作版本号
     * 项目启动时从命令行传入，例如：java -jar xxx.war --version=202008
     * TODO 在yml中增加版本号
     */
    private String operVer = "0000";

    @Resource
    private NorLogDao norLogDao;

    /**
     * 设置操作日志切入点 记录操作日志 在注解的位置切入代码
     * TODO 需要修改包路径
     */
    @Pointcut("@annotation(com.yjt.common.log.annotation.OperLog)")
    public void operLogPoinCut() {
    }

    /**
     * 设置操作异常切入点记录异常日志 扫描所有controller包下操作
     * TODO 需要修改包路径
     */
    @Pointcut("within(com.yjt.*.controller..*)")
    public void operExceptionLogPoinCut() {
    }

    /**
     * 正常返回通知，拦截用户操作日志，连接点正常执行完成后执行， 如果连接点抛出异常，则不会执行
     *
     * @param joinPoint 切入点
     * @param keys      返回结果
     */
    @AfterReturning(value = "operLogPoinCut()", returning = "keys")
    public void saveOperLog(JoinPoint joinPoint, Object keys) {
        // 获取RequestAttributes
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        NormalLog operlog = this.getNormalLog(requestAttributes, joinPoint, keys, operVer);
        norLogDao.insert(operlog);
    }

    /**
     * 获取正常日志实体
     *
     * @param joinPoint
     * @return
     */
    public NormalLog getNormalLog(RequestAttributes requestAttributes, JoinPoint joinPoint,
                                  Object keys, String operVer) {
        try {
            // 从获取RequestAttributes中获取HttpServletRequest的信息
            HttpServletRequest request = (HttpServletRequest) requestAttributes
                    .resolveReference(RequestAttributes.REFERENCE_REQUEST);
            NormalLog operlog = NormalLog.builder().build();
            operlog.setOperId(UUID.randomUUID().toString().replace("-", ""));
            // 从切面织入点处通过反射机制获取织入点处的方法
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            // 获取切入点所在的方法
            Method method = signature.getMethod();
            getAnnotationInfo(operlog, method);
            // 获取请求的类名
            String className = joinPoint.getTarget().getClass().getName();
            // 获取请求的方法名
            String methodName = method.getName();
            methodName = className + "." + methodName;
            // 请求的参数
            Map<String, String> rtnMap = converMap(request.getParameterMap());
            // 将参数所在的数组转换成json
            String params = JSON.toJSONString(rtnMap);
            //获取用户信息
            CommonUser commonUser = SecurityUtils.getCurrentUser();
            String userId = commonUser.getEmplyId();
            String userName = commonUser.getEmplyName();
            //请求方法,请求参数, 返回结果
            operlog.setOperMethod(methodName)
                    .setOperRequParam(params)
                    .setOperRespParam(JSON.toJSONString(keys))
                    .setOperUserId(userId)
                    // 请求用户名称
                    .setOperUserName(userName)
                    // 请求IP
                    .setOperIp(getRemortIP(request))
                    // 请求URI
                    .setOperUri(request.getRequestURI())
                    // 创建时间
                    .setOperCreateTime(LocalDateTime.now());
            return operlog;
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }

    private void getAnnotationInfo(NormalLog operlog, Method method) {
        // 获取操作
        OperLog opLog = method.getAnnotation(OperLog.class);
        if (opLog != null) {
            String operModul = opLog.operModul().name();
            String operType = opLog.operType().name();
            String operDesc = opLog.operDesc();
            //操作模块,操作类型,操作描述
            operlog.setOperModul(operModul).setOperType(operType).setOperDesc(operDesc);
        }
    }


    /**
     * 转换request 请求参数
     *
     * @param paramMap request获取的参数数组
     */
    public static Map<String, String> converMap(Map<String, String[]> paramMap) {
        Map<String, String> rtnMap = new HashMap<String, String>();
        for (String key : paramMap.keySet()) {
            rtnMap.put(key, paramMap.get(key)[0]);
        }
        return rtnMap;
    }

    /**
     * 获取远程ip
     *
     * @param request
     * @return
     */
    private static String getRemortIP(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }


}
